#pragma once
#include "match.hpp"

const std::string webroot = "./wwwroot";
const int port = 8085;

class gobang_server
{
public:
    gobang_server()
    :_webroot(webroot)
    ,_rm(&_table,&_online)
    ,_match(&_table,&_rm,&_online)
    ,_sm(&_server)
    {
        _server.set_access_channels(websocketpp::log::alevel::none);
        _server.init_asio();
        _server.set_reuse_addr(true);
        _server.set_http_handler(std::bind(&gobang_server::http_callback, this, std::placeholders::_1));
        _server.set_open_handler(std::bind(&gobang_server::open_callback, this, std::placeholders::_1));
        _server.set_close_handler(std::bind(&gobang_server::close_callback, this, std::placeholders::_1));
        _server.set_message_handler(std::bind(&gobang_server::message_callback, this, std::placeholders::_1, std::placeholders::_2));
    }

    ~gobang_server()
    {   

    }

    void http_resp(wsserver_t::connection_ptr& conn,bool res,websocketpp::http::status_code::value code,const std::string& rs)
    {
        Json::Value resp;
        resp["result"] = res;
        resp["reason"] = rs;
        std::string body;
        json_util::serialize(resp,body);
        conn->set_body(body);
        conn->set_status(code);
        conn->append_header("Content-Type","application/json");
    }

    void reg(wsserver_t::connection_ptr& conn)
    {
        std::string body =  conn->get_request_body();
        Json::Value user;
        json_util::deserialize(body,user);
        if(user["username"].asString() == "" || user["password"].asString() == "")
        {
            http_resp(conn,false,websocketpp::http::status_code::ok,"用户密码不能为空!");
            return;
        }   
        if(!_table.insert(user))
        {
            http_resp(conn,false,websocketpp::http::status_code::ok,"用户名重复!");
            return;
        }
        http_resp(conn,true,websocketpp::http::status_code::ok,"注册成功!");
    }

    void login(wsserver_t::connection_ptr& conn)
    {
        std::string body =  conn->get_request_body();
        Json::Value user;
        json_util::deserialize(body,user);
        if(user["username"].asString() == "" || user["password"].asString() =="")
        {
            http_resp(conn,false,websocketpp::http::status_code::ok,"用户名密码不能为空!");
            return;
        }
        if(!_table.login(user))
        {
            http_resp(conn,false,websocketpp::http::status_code::ok,"用户名密码错误!");
            return;
        }
        http_resp(conn,true,websocketpp::http::status_code::ok,"登陆成功!");
        _table.select_by_name(user["username"].asString(),user);
        int uid = user["id"].asInt();
        session_ptr sp = _sm.create_session(uid,true);
        _sm.set_session_timer(uid,30000);
        std::string cookie = "sid=" + std::to_string(sp->get_sid());
        conn->append_header("Set-Cookie",cookie);
    }   

    int get_sid_from_cookie(const std::string& cookie)
    {
        std::vector<std::string> v;
        string_util::split(cookie,"; ",v);
        for(const auto& e : v)
        {
            std::vector<std::string> tempv;
            string_util::split(e,"=",tempv); 
            if(tempv[0] == "sid")
            {   
                return std::stoi(tempv[1]);
            }
        }
        return 0;
    }

    void info(wsserver_t::connection_ptr& conn)
    {
        std::string cookie = conn->get_request_header("Cookie");
        DLOG("cookie:%s",cookie.c_str());
        if(cookie.empty())
        {
            http_resp(conn,false,websocketpp::http::status_code::ok,"找不到cookie信息,请重新登陆!");
            return;
        }
        int sid = get_sid_from_cookie(cookie);
        if(sid == 0)
        {
            http_resp(conn,false,websocketpp::http::status_code::ok,"找不到sid信息,请重新登陆!");
            return;
        }
        session_ptr sp = _sm.get_session_by_sid(sid);
        if(sp.get() == nullptr)
        {
            http_resp(conn,false,websocketpp::http::status_code::ok,"找不到对应session信息,请重新登陆!");
            return;
        }
        //登陆认证成功，进行用户信息发送
        int uid =  sp->get_uid();
        Json::Value resp;
        _table.select_by_id(uid,resp);
        resp["result"] = true;
        std::string body;
        json_util::serialize(resp,body);
        conn->set_body(body);
        conn->set_status(websocketpp::http::status_code::ok);
        conn->append_header("Content-Type","application/json");      
        _sm.set_session_timer(sid,30000);
    }

    void file_handler(wsserver_t::connection_ptr& conn,const std::string& uri)
    {
        std::string filepath;
        if(uri == "/")
        {
            filepath = _webroot + "/login.html";
        }
        else
        {
            filepath = _webroot + uri;
        }
        std::string body;
        bool ret = file_util::read(filepath,body);
        if(ret == false)
        {
            filepath = _webroot + "/not_found.html";
            file_util::read(filepath,body);
        }
        conn->set_body(body);
        if(ret == false)
        {
            conn->set_status(websocketpp::http::status_code::not_found);
        }
        else
        {
            conn->set_status(websocketpp::http::status_code::ok);
        }
    }

    void http_callback(websocketpp::connection_hdl hdl)
    {
        wsserver_t::connection_ptr conn = _server.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req  = conn->get_request();
        std::string method = req.get_method();
        std::string uri = req.get_uri();
        if(method == "POST" && uri == "/reg")
        {   
            reg(conn);
        }
        else if(method == "POST" && uri == "/login")
        {
            login(conn);
        }
        else if(method == "GET" && uri == "/info")
        {
            info(conn);
        }
        else
        {
            file_handler(conn,uri);
        }
    }

    void ws_resp(wsserver_t::connection_ptr& conn,const Json::Value& resp) 
    {
        std::string body;
        json_util::serialize(resp, body);
        conn->send(body);
    }

    //登陆认证,用于websocket连接成功后的回调
    session_ptr get_session_by_con(wsserver_t::connection_ptr& conn) 
    {
        Json::Value resp;
        std::string cookie = conn->get_request_header("Cookie");
        if (cookie.empty()) 
        {
            resp["optype"] = "hall_ready";
            resp["reason"] = "没有找到cookie信息,请重新登录!";
            resp["result"] = false;
            ws_resp(conn, resp);
            return session_ptr();
        }
        int sid = get_sid_from_cookie(cookie);
        if (sid == 0) 
        {
            resp["optype"] = "hall_ready";
            resp["reason"] = "没有找到sid信息,请重新登录!";
            resp["result"] = false;
            ws_resp(conn,resp);
            return session_ptr();
        }
        session_ptr sp = _sm.get_session_by_sid(sid);
        if(sp.get() == nullptr) 
        {
            resp["optype"] = "hall_ready";
            resp["reason"] = "没有找到session信息,请重新登录!";
            resp["result"] = false;
            ws_resp(conn,resp);
            return session_ptr();
        }
        return sp;
    }

    void wsopen_game_hall(wsserver_t::connection_ptr& conn)
    {
        session_ptr sp = get_session_by_con(conn);
        if(sp.get() == nullptr)
        {
            return;
        }
        int uid =  sp->get_uid();
        Json::Value  resp;
        _online.enter_game_hall(uid,conn);
        DLOG("新增用户%d进入游戏大厅!",uid);
        resp["optype"] = "hall_ready";
        resp["result"] = true;
        ws_resp(conn,resp);
        _sm.set_session_timer(sp->get_sid(),-1);
    }

    void wsopen_game_room(wsserver_t::connection_ptr& conn)
    {
        session_ptr sp = get_session_by_con(conn);
        int uid = sp->get_uid();
        room_ptr rp = _rm.get_room_by_uid(uid);
        _online.enter_game_room(uid,conn);
        _sm.set_session_timer(sp->get_sid(),-1);
        Json::Value resp;
        resp["optype"] = "room_ready";
        resp["result"] = true;
        resp["room_id"] = rp->get_rid();
        resp["uid"] = uid;
        resp["white_id"] = rp->get_wid();
        resp["black_id"] = rp->get_bid();
        ws_resp(conn,resp);
    }

    void open_callback(websocketpp::connection_hdl hdl)
    {
        wsserver_t::connection_ptr conn = _server.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req = conn->get_request();
        std::string uri = req.get_uri();
        if(uri == "/hall")
        {
            wsopen_game_hall(conn);
        }
        else if(uri == "/room")
        {
            wsopen_game_room(conn);
        }
    }

    void wsclose_game_hall(wsserver_t::connection_ptr& conn)
    {
        session_ptr sp = get_session_by_con(conn);
        _online.exit_game_hall(sp->get_uid());
        _sm.set_session_timer(sp->get_sid(),30000);
    }

    void wsclose_game_room(wsserver_t::connection_ptr& conn)
    {
        session_ptr sp = get_session_by_con(conn);
        int uid = sp->get_uid();
        _online.exit_game_room(uid);
        _sm.set_session_timer(sp->get_sid(),30000);
        _rm.remove_room_user(uid);
    }

    void close_callback(websocketpp::connection_hdl hdl)
    {
        wsserver_t::connection_ptr conn = _server.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req = conn->get_request();
        std::string uri = req.get_uri();
        if(uri == "/hall")
        {
            wsclose_game_hall(conn);
        }
        else if(uri == "/room")
        {
            wsclose_game_room(conn);
        }
    }

    void message_game_hall(wsserver_t::connection_ptr& conn,wsserver_t::message_ptr& msg)
    {
        session_ptr sp = get_session_by_con(conn);
        int uid = sp->get_uid();
        std::string body(msg->get_payload());
        Json::Value req;
        json_util::deserialize(body,req);
        std::string type(req["optype"].asString());
        Json::Value resp;
        if(type  == "match_start")
        {
            _match.add(uid);
            resp["optype"] = "match_start";
            resp["result"] = true;
            resp["reason"] = "开始匹配!";
        }
        else if(type  == "match_stop")
        {
            _match.del(uid);
            resp["optype"] = "match_stop";
            resp["result"] = true;
            resp["reason"] = "停止匹配!";
        }
        ws_resp(conn,resp);
    }

    void message_game_room(wsserver_t::connection_ptr& conn,wsserver_t::message_ptr& msg)
    {
        session_ptr sp = get_session_by_con(conn);
        room_ptr rp =  _rm.get_room_by_uid(sp->get_uid());
        Json::Value req;
        std::string body = msg->get_payload();
        json_util::deserialize(body,req);
        rp->handle_request(req);
    }

    void message_callback(websocketpp::connection_hdl hdl,wsserver_t::message_ptr msg)
    {
        wsserver_t::connection_ptr conn = _server.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req = conn->get_request();
        std::string uri = req.get_uri();
        if(uri == "/hall")
        {
            message_game_hall(conn,msg);
        }
        else if(uri == "/room")
        {
            message_game_room(conn,msg);
        }
    }

    void start()
    {
        _server.listen(port);
        _server.start_accept();
        _server.run();
    }

private:
    std::string _webroot;
    wsserver_t _server;
    user_table _table;
    online_manager _online;
    room_manager _rm;
    match _match;
    session_manager _sm;
};